﻿using Brc_DSB.GetWay.Configuration;
using Microsoft.AspNetCore.Http;
using Ocelot.Cache;
using Ocelot.Logging;
using Ocelot.Middleware;
using System;
using System.Collections.Generic;
using System.IO;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using Brc_DSB.GetWay.Extention;
using Ocelot.Middleware.Pipeline;
using Newtonsoft.Json.Linq;
using Ocelot.Configuration;

namespace Brc_DSB.GetWay.Middleware
{

    public static class OutputCacheMiddlewareExtensions
    {
        public static IOcelotPipelineBuilder UseBrcOutputCacheMiddleware(this IOcelotPipelineBuilder builder)
        {
            return builder.UseMiddleware<OutputCacheMiddleware>();
        }
    }
    public class OutputCacheMiddleware : OcelotMiddleware
    {
        private readonly OcelotRequestDelegate _next;
        private readonly IOcelotCache<CachedResponse> _outputCache;

        public OutputCacheMiddleware(OcelotRequestDelegate next,
            IOcelotLoggerFactory loggerFactory,
            IOcelotCache<CachedResponse> outputCache
            )
                : base(loggerFactory.CreateLogger<OutputCacheMiddleware>())
        {
            _next = next;
            _outputCache = outputCache;
        }

        public async Task Invoke(DownstreamContext httpContext)
        {
            var downstreamRoute = httpContext.DownstreamReRoute;
            if (!downstreamRoute.IsCached)
            {
                await _next.Invoke(httpContext);
                return;
            }
            var downstreamRequest = httpContext.DownstreamRequest;
            var downstreamUrlKey = $"{downstreamRequest.Method}_{downstreamRequest.OriginalString.Replace(":", "_")}";
            var cached = _outputCache.Get(downstreamUrlKey, downstreamRoute.CacheOptions.Region);
            if (cached != null)
            {
                var response = CreateHttpResponseMessage(cached);
                httpContext.DownstreamResponse = response;
                return;
            }
            await _next.Invoke(httpContext);
            if (httpContext.IsError)
            {
                return;
            }
            var downstreamResponse = httpContext.DownstreamResponse;
            await CreateCachedResponse(downstreamResponse, downstreamUrlKey, downstreamRoute, downstreamRequest.Method);
        }

        private void SetHttpResponseMessageThisRequest(DownstreamContext context, DownstreamResponse response)
        {
            context.HttpContext.Items.UpsertDownstreamResponse(response);
        }

        internal DownstreamResponse CreateHttpResponseMessage(CachedResponse cached)
        {
            if (cached == null)
            {
                return null;
            }
            var content = new MemoryStream(System.Text.Encoding.UTF8.GetBytes(cached.Body));
            var streamContent = new StreamContent(content);
            foreach (var header in cached.ContentHeaders)
            {
                streamContent.Headers.TryAddWithoutValidation(header.Key, header.Value);
            }
            return new DownstreamResponse(streamContent, cached.StatusCode, cached.Headers, cached.ReasonPhrase);
        }

        /// <summary>
        /// 将响应存进缓存
        /// </summary>
        /// <param name="response">响应消息</param>
        /// <param name="downstreamRequestMethod">请求HTTP谓词</param>
        /// <param name="downstreamRoute">路由配置信息（主要取缓存相关配置）</param>
        /// <param name="key">缓存键</param>
        /// <param name="paramter">非GET请求的参数</param>
        /// <returns></returns>
        internal async Task CreateCachedResponse(DownstreamResponse response, string key,DownstreamReRoute downstreamReRoute,string downstreamRequestMethod)
        {
            if (response == null)
                return;
            var statusCode = response.StatusCode;
            var headers = response.Headers.ToDictionary();
            string body = null;
            JToken value = null;
            bool trySuccess = false;
            if (response.Content != null)
            {
                var content = await response.Content.ReadAsByteArrayAsync();
                body = System.Text.Encoding.Default.GetString(content);
                try
                {
                    trySuccess = body.ToJObject().TryGetValue("isSuccess", StringComparison.CurrentCultureIgnoreCase, out value);
                }
                catch(Exception e)
                {
                    Logger.LogError("缓存中间件在转换响应获取isSuccess时出错", e);
                    return;
                }
            }
            var region = downstreamReRoute.CacheOptions.Region;
            var ttlSeconds = downstreamReRoute.CacheOptions.TtlSeconds;
            //没有出现异常
            if (trySuccess && Convert.ToBoolean(value.ToString()))
            {
                //如果不是GET请求
                if (!downstreamRequestMethod.Equals("GET", StringComparison.CurrentCultureIgnoreCase))
                    return;
                var contentHeaders = response?.Content?.Headers.ToDictionary();
                var cached = new CachedResponse(statusCode, headers, body, contentHeaders, response.ReasonPhrase);
                _outputCache.Add(key, cached, TimeSpan.FromSeconds(ttlSeconds), region);
            }
        }
    }
}
